package com.itheima.tliaswebmanagement.aop;

import com.alibaba.fastjson2.JSONObject;
import com.itheima.tliaswebmanagement.mapper.OperateLogMapper;
import com.itheima.tliaswebmanagement.pojo.OperateLog;
import com.itheima.tliaswebmanagement.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Arrays;

@Slf4j
@Aspect
@Component
public class LogAspect {
    private final HttpServletRequest httpServletRequest;
    private final SqlSessionFactory sqlSessionFactory;

    @Autowired
    public LogAspect(HttpServletRequest httpServletRequest, SqlSessionFactory sqlSessionFactory) {
        log.info("记录日志的切面已创建~");
        this.httpServletRequest = httpServletRequest;
        this.sqlSessionFactory = sqlSessionFactory;
    }

    @Around("@annotation(com.itheima.tliaswebmanagement.anno.RecordOperationLog)")
    public Object recordLog(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("进入切入点...");
        // 登录成功后所有的请求会携带jwt令牌，令牌中包含用户id、name、username
        String jwt = httpServletRequest.getHeader("token");
        Claims claims = JwtUtils.parseJwt(jwt);
        Object userId = claims.get("id");// 操作人id
        // 获取操作类名
        String className = proceedingJoinPoint
                .getTarget()
                .getClass()
                .getName();

        // 获取操作方法名
        String methodName = proceedingJoinPoint
                .getSignature()
                .getName();

        // 获取参数列表的字符串形式
        String methodArgs = Arrays.toString(proceedingJoinPoint.getArgs());

        // 获取当前时间
        LocalDateTime operateTime = LocalDateTime.now();// 获取当前时间的 LocalDateTime 对象
        // 运行并获取返回值
        Object result = proceedingJoinPoint.proceed();
        String returnValue = JSONObject.toJSONString(result);
        // 目标方法结束时间毫秒值
        long end = System.currentTimeMillis();

        // 通过此前记录的起始时间获取毫秒值，分毫不差
        Instant instant = operateTime.atZone(ZoneId.systemDefault()).toInstant();// 将当前时间的 LocalDateTime 对象转换为 Instant 对象
        long begin = instant.toEpochMilli();// 起始时间毫秒值

        OperateLog operateLog = OperateLog.builder()
                .operateUser((Integer) userId)// 设置操作用户id
                .operateTime(operateTime)// 设置操作时间
                .className(className)// 设置操作类名
                .methodName(methodName)// 设置操作方法名
                .methodParams(methodArgs)// 设置操作方法参数
                .returnValue(returnValue)// 设置操作方法返回值
                .costTime(end - begin)// 设置操作耗时
                .build();

        try (SqlSession sqlSession = sqlSessionFactory.openSession(true)) {
            log.info("已记录日志：{}", operateLog);
            OperateLogMapper operateLogMapper = sqlSession.getMapper(OperateLogMapper.class);
            operateLogMapper.insert(operateLog);// 记录于数据库
        }
        return result;// 返回值一定要和操作方法的返回值一致!!!
    }
}
